package com.tianhua.javs.core.factory;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tianhua.javs.client.bean.*;
import com.tianhua.javs.client.enums.BusinessKeyWardEnum;
import com.tianhua.javs.client.enums.VariablePoolTypeEnum;
import com.tianhua.javs.client.model.AbstractClassModel;
import com.tianhua.javs.client.model.FieldModel;
import com.tianhua.javs.client.model.MethodModel;
import com.tianhua.javs.core.constant.TagConstants;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Description: 变量工厂
 * date: 2022/2/12
 *
 * @author shenshuai
 * @version 1.0.0
 * @since JDK 1.8
 */
@Service
public class VariableFactory {

    /**
     * 构建当前方法的变量池
     * 在进行方法翻译的时候构建
     *
     * @param javsTranslateContextBean
     * @param javsScriptMethodBean
     */
    public Map<String, List<ScriptVarMappingBean>> initVarPool(JavsTranslateContextBean javsTranslateContextBean, JavsScriptMethodBean javsScriptMethodBean) {
        Map<String, List<ScriptVarMappingBean>> scriptMappingBeanMap = new HashMap<>();
        List<ScriptVarMappingBean> scriptVarMappingBeanList = Lists.newArrayList();
        if (CollectionUtils.isEmpty(javsScriptMethodBean.getParamBeanList())) {
            scriptMappingBeanMap.putAll(initJavaSDKTypeVarMapping());
            return scriptMappingBeanMap;
        }


        for (JavsParamBean javsParamBean : javsScriptMethodBean.getParamBeanList()) {
            //处理bo类可能存在的变量
            if (javsParamBean.isGenerateBO()) {
                JavsModelBean javsModelBean = javsTranslateContextBean.getJavsModelBeanMap(BusinessKeyWardEnum.BO.getKeyWardCode()).get(javsParamBean.getParamType());
                scriptVarMappingBeanList.addAll(dealBoVarPool(javsParamBean, javsModelBean));
            }

            //处理do类可能存在的变量
            if (javsParamBean.isGenerateDO()) {
                JavsModelBean javsModelBean = javsTranslateContextBean.getJavsModelBeanMap(BusinessKeyWardEnum.DO.getKeyWardCode()).get(javsParamBean.getParamType());
                scriptVarMappingBeanList.addAll(dealDoVarPool(javsParamBean, javsModelBean));
            }
        }
        scriptVarMappingBeanList.addAll(dealMethodParamVarMapping(javsScriptMethodBean.getParamBeanList()));

        scriptMappingBeanMap.put(VariablePoolTypeEnum.BUSINESS_VAR.getCode(),scriptVarMappingBeanList);
        scriptMappingBeanMap.putAll(initJavaSDKTypeVarMapping());

        return scriptMappingBeanMap;
    }


    /**
     * 处理构建BO关联的变量池信息
     *
     * @param javsParamBean
     * @param javsModelBean
     */
    private  List<ScriptVarMappingBean> dealBoVarPool(JavsParamBean javsParamBean, JavsModelBean javsModelBean) {
        List<ScriptVarMappingBean> businessVarList = Lists.newArrayList();
        if (javsModelBean == null) {
            return businessVarList;
        }

        String javsVarName = javsParamBean.getJavsParamName();
        List<ScriptVarMappingBean> fieldVarList = buildFieldVarMapping(javsVarName, javsParamBean.getOriginParamName(), javsModelBean.getFieldModelList());
        businessVarList.addAll(fieldVarList);
        List<ScriptVarMappingBean> methodVarList = buildMethodVarMapping(javsVarName, javsParamBean.getOriginParamName(), javsModelBean.getMethodModelList());
        businessVarList.addAll(methodVarList);

        String gatawayElement = null, repositoryElement = null, factoryElement = null, serviceElement = null;

        //如果只是bo来代表具体参数对象,则当前bo对应的服务类默认只有一个，如果有多个的话就走bo.xxxRepository这种形式
        if (javsParamBean.isStrictBO()) {
            gatawayElement = BusinessKeyWardEnum.GATAWAY.getKeyWardCode();
            repositoryElement = BusinessKeyWardEnum.REPOSITORY.getKeyWardCode();
            factoryElement = BusinessKeyWardEnum.FACTORY.getKeyWardCode();
            serviceElement = BusinessKeyWardEnum.SERVICE.getKeyWardCode();
        }

        List<ScriptVarMappingBean> gatawayVarList = buildServiceVarMapping(javsVarName, javsModelBean.getGatawayList(), gatawayElement);
        businessVarList.addAll(gatawayVarList);

        List<ScriptVarMappingBean> serviceVarList = buildServiceVarMapping(javsVarName, javsModelBean.getServiceList(), serviceElement);
        businessVarList.addAll(serviceVarList);

        List<ScriptVarMappingBean> repositoryVarList = buildServiceVarMapping(javsVarName, javsModelBean.getRepositoryList(), repositoryElement);
        businessVarList.addAll(repositoryVarList);

        List<ScriptVarMappingBean> factoryVarList = buildServiceVarMapping(javsVarName, javsModelBean.getFactoryList(), factoryElement);
        businessVarList.addAll(factoryVarList);

        List<ScriptVarMappingBean> convertDOVarList = buildConvertVarMapping(javsVarName, javsModelBean.getConvertdo(), Lists.newArrayList("convertdo", "convertdos"));
        businessVarList.addAll(convertDOVarList);

        List<ScriptVarMappingBean> convertDTOVarList = buildConvertVarMapping(javsVarName, javsModelBean.getConvertdto(), Lists.newArrayList("convertdto", "convertdtos"));
        businessVarList.addAll(convertDTOVarList);
        //将方法参数放最后
        ScriptVarMappingBean scriptVarMappingBean = ScriptVarMappingBean.build(javsParamBean);
        businessVarList.add(scriptVarMappingBean);

        return businessVarList;
    }


    /**
     * 处理构建DO关联的变量池信息
     *
     * @param javsParamBean
     * @param javsModelBean
     */
    private  List<ScriptVarMappingBean> dealDoVarPool(JavsParamBean javsParamBean, JavsModelBean javsModelBean) {
        List<ScriptVarMappingBean> varList = Lists.newArrayList();
        if (javsModelBean == null) {
            return  varList;
        }
        ScriptVarMappingBean scriptVarMappingBean = ScriptVarMappingBean.build(javsParamBean);

        varList.add(scriptVarMappingBean);

        String javsVarName = javsParamBean.getJavsParamName();
        List<ScriptVarMappingBean> fieldVarList = buildFieldVarMapping(javsVarName, javsParamBean.getOriginParamName(), javsModelBean.getFieldModelList());
        varList.addAll(fieldVarList);
        List<ScriptVarMappingBean> methodVarList = buildMethodVarMapping(javsVarName, javsParamBean.getOriginParamName(), javsModelBean.getMethodModelList());
        varList.addAll(methodVarList);

        List<ScriptVarMappingBean> convertVarList = buildConvertVarMapping(javsVarName, javsModelBean.getConvertbo(), Lists.newArrayList("convertbo", "convertbos"));
        varList.addAll(convertVarList);
        return varList;
    }


    /**
     * 构建这个对象关联的业务属性
     *
     * @param javsVarName
     * @param javaVarName
     * @param fieldModelList
     * @return
     */
    private List<ScriptVarMappingBean> buildFieldVarMapping(String javsVarName, String javaVarName, List<FieldModel> fieldModelList) {
        List<ScriptVarMappingBean> varList = Lists.newArrayList();
        for (FieldModel fieldModel : fieldModelList) {
            String javsVarCode = javsVarName + "." + fieldModel.getFieldSimpleName();
            ScriptVarMappingBean getMethodVarMappingBean = ScriptVarMappingBean.getInstance(javsVarCode, javaVarName + "." + fieldModel.getGetterMethod());
            getMethodVarMappingBean.setTag("get");
            getMethodVarMappingBean.setVarClassName(fieldModel.getFieldType());
            varList.add(getMethodVarMappingBean);

            ScriptVarMappingBean setMethodVarMappingBean = ScriptVarMappingBean.getInstance(javsVarCode, javaVarName + "." + fieldModel.getSetterMethod());
            setMethodVarMappingBean.setTag("set");
            setMethodVarMappingBean.setVarClassName(fieldModel.getFieldType());
            varList.add(getMethodVarMappingBean);


            String methodFuncVarCode = "(" + fieldModel.getFieldSimpleName() + ")";
            ScriptVarMappingBean getMethodFuncVarMappingBean = ScriptVarMappingBean.getInstance(methodFuncVarCode, fieldModel.getClassName() + "::" + fieldModel.getGetterMethodSimple());
            getMethodFuncVarMappingBean.setTag(fieldModel.getClassName());
            getMethodFuncVarMappingBean.setVarClassName(fieldModel.getFieldType());
            getMethodFuncVarMappingBean.setReferClassName(fieldModel.getClassName());

            varList.add(getMethodFuncVarMappingBean);
        }
        return varList;
    }


    /**
     * 构建这个对象关联的本身具有的方法
     *
     * @param javsVarName
     * @param serviceModelList
     * @param classElement
     * @return
     */
    private List<ScriptVarMappingBean> buildServiceVarMapping(String javsVarName, List<AbstractClassModel> serviceModelList, String classElement) {
       List<ScriptVarMappingBean> varList = Lists.newArrayList();

        for (AbstractClassModel classModel : serviceModelList) {
            //如果是空说明就是bo.xxxRepository或者bo.xxxFactory这样的引用形式
            if (StringUtils.isEmpty(classElement)) {
                //将其用具体类变量进行引用
                classElement = classModel.getClassVarIdentifer();
            }
            String javsVarCode = javsVarName + "." + classElement;

            ScriptVarMappingBean classVarMappingBean = ScriptVarMappingBean.getInstance(javsVarCode, classModel.getClassVarIdentifer());
            classVarMappingBean.setVarClassName(classModel.getClassName());
            classVarMappingBean.setTag(classElement);
            varList.add(classVarMappingBean);
        }
        return varList;
    }


    /**
     * 构建这个对象的转换服务变量
     *
     * @param javsVarName
     * @param convertInterfaceModel
     * @param classElementList
     * @return
     */
    private List<ScriptVarMappingBean> buildConvertVarMapping(String javsVarName, AbstractClassModel convertInterfaceModel, List<String> classElementList) {
        List<ScriptVarMappingBean> varList = Lists.newArrayList();
        if (convertInterfaceModel == null) {
            return varList;
        }
        for (String classElement : classElementList) {
            String javsVarCode = javsVarName + "." + classElement;

            //xxxbo.convertdo() --->xxxConvert.bo2do()
            //xxxbo.convertdos()
            //xxxbo.convertdos()
            //由于convert方法本身的灵活性太强，而且是动态生成的，所以这里将原转换接口作为变量池的引用进行动态判断构建转换方法内容
            ScriptVarMappingBean convertVarMappingBean = ScriptVarMappingBean.getInstance(javsVarCode, convertInterfaceModel.getClassName());
            convertVarMappingBean.setReferClassModel(convertInterfaceModel);
            varList.add(convertVarMappingBean);
        }

        return varList;
    }


    /**
     * 构建这个对象关联的本身具有的方法
     *
     * @param javsVarName
     * @param javaVarName
     * @param methodModelList
     * @return
     */
    private  List<ScriptVarMappingBean> buildMethodVarMapping(String javsVarName, String javaVarName, List<MethodModel> methodModelList) {
        List<ScriptVarMappingBean> varList = Lists.newArrayList();
        for (MethodModel methodModel : methodModelList) {
            String javsVarCode = javsVarName + "." + methodModel.getMethdSimpleName();
            ScriptVarMappingBean methodVarMappingBean = ScriptVarMappingBean.getInstance(javsVarCode, javaVarName + TagConstants.POINT + methodModel.getMethdSimpleName());
            methodVarMappingBean.setTag(methodModel.getParamModelList().size() + "");
            varList.add(methodVarMappingBean);

            String methodFuncVarCode = TagConstants.BRACKET_LEFT + methodModel.getMethdSimpleName() + TagConstants.BRACKET_RIGHT;
            ScriptVarMappingBean methodFuncVarMappingBean = ScriptVarMappingBean.getInstance(methodFuncVarCode, methodModel.getClassName() + TagConstants.TWO_COLON + methodModel.getMethdSimpleName() + TagConstants.BRACKET_ALL);
            methodFuncVarMappingBean.setTag(methodModel.getClassName());
            methodFuncVarMappingBean.setReferClassName(methodModel.getClassName());
            varList.add(methodFuncVarMappingBean);
        }
        return varList;
    }

    /**
     * 将方法参数当作变量对应关系
     *
     * @param javsParamBeanList
     * @return
     */
    private  List<ScriptVarMappingBean> dealMethodParamVarMapping(List<JavsParamBean> javsParamBeanList) {
        List<ScriptVarMappingBean> scriptVarList = Lists.newArrayList();
        for (JavsParamBean javsParamBean : javsParamBeanList) {
            ScriptVarMappingBean scriptVarMappingBean = ScriptVarMappingBean.build(javsParamBean);
            scriptVarList.add(scriptVarMappingBean);
        }
        return scriptVarList;
    }


    /**
     * 构建Java sdk原生变量辅助解析javs脚本赋值语句等场景
     * @return
     */
    public Map<String, List<ScriptVarMappingBean>> initJavaSDKTypeVarMapping(){
        Map<String, List<ScriptVarMappingBean>> javaSdkTypeVarMap = Maps.newHashMap();
        List<ScriptVarMappingBean> javaSDKTypeVarList = new ArrayList<>();

        /**
         * 赋值语句的场景
         * 1.
         *  aInt = 1
         *     aLong = 1L
         *     bStr = "b"
         *     cArr = new String[2];
         *     lista = list() -->默认arraylist
         *     listb = list<str>()
         *     listc = linkedlist()
         *     listd = linkedlist<long>()
         *
         *
         *     mapa = map()  -->默认hashmap
         *     mapb = map<str,obj>()
         *     mapc = treemap()
         *     mapd = treemap<str,obj>()
         *
         *
         *     seta = set()  -->默认hashset
         *     setb = set<str>()
         *     setc = treeset()
         *     setd = treeset<str>()
         *     xxx.aField = yyy.bField
         *     xxx.aField = yyy.method()
         *     result = yyy.method()
         */
        //
       /* javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("map()","Map","Maps.newHashMap()"));
        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("map<","Map","new HashMap<"));

        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("treemap()","Map","Maps.newTreeMap()"));
        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("treemap<","Map","new TreeMap<"));


        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("set()","Set"," Sets.newHashSet()"));
        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("set<","Set","new HashSet<"));
*/

        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("str","String","String"));
        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("obj","Object","Object"));

        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("long","Long","Long"));
        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("int","Integer","Integer"));
        javaSDKTypeVarList.add(ScriptVarMappingBean.getInstance("bool","Boolean","Boolean"));




        List<ScriptVarMappingBean> javaModifierVarList = new ArrayList<>();

        javaModifierVarList.add(ScriptVarMappingBean.getInstance("publicsf","public static final",""));
        javaModifierVarList.add(ScriptVarMappingBean.getInstance("publics","public static",""));
        javaModifierVarList.add(ScriptVarMappingBean.getInstance("publicf","public final",""));

        javaModifierVarList.add(ScriptVarMappingBean.getInstance("privatesf","private static final",""));
        javaModifierVarList.add(ScriptVarMappingBean.getInstance("privates","private static",""));
        javaModifierVarList.add(ScriptVarMappingBean.getInstance("privatef","private final",""));


        javaSdkTypeVarMap.put(VariablePoolTypeEnum.JAVA_SDK.getCode(),javaSDKTypeVarList);
        javaSdkTypeVarMap.put(VariablePoolTypeEnum.JAVA_SDK_MODIFIER.getCode(),javaSDKTypeVarList);

        return javaSdkTypeVarMap;
    }


}
